# koroFileHeader at Yungoal acer
# Create: 2022-11-21 10:59:23
# LastEdit: 2022-11-21 18:18:29
"""自动录入CPP采购数量"""
__author__ = '749B'


if __name__ == '__main__':
    # 导入路径
    from pathlib import Path
    BASE_DIR = Path(__file__).resolve().parent.parent
    import sys
    sys.path.append(BASE_DIR.__str__())
    # Django 初始化
    import django
    from django.conf import settings
    from azure_ea_insights import settings as my_settings
    settings.configure(**{ k: v for k, v in vars(my_settings).items() if k.isupper() })
    django.setup()


import datetime
import time
import logging

from django.db.models import Sum

from billing import models

from tasks.thread_task import BaseTask


class CppSync(BaseTask):
    """根据最近一个完整月的数据，计算出CPP的购买数量
    买了没用，肯定算不到。
    买了用不满，不会影响结果。
    但是如果只用了一点点，小于一个度量单位，具体看机型的定价，1 or 10 or 100 会导致少1个
    """

    def __init__(self, account_name: str='FromTasks') -> None:
        super().__init__(task_name=account_name)
        self.start_date, self.end_date = self.get_star_end_date()
        self.logger = logging.getLogger("tasks.CppSync")
        self.logger.success = lambda msg, *args, **kwargs: self.logger.log(25, msg, *args, **kwargs)  # type: ignore

    @staticmethod
    def get_star_end_date():
        """最新的一个完整月数据的开始、结束日期"""
        last_date = models.Detail.objects.latest('date').date
        if (last_date + datetime.timedelta(days=1)).month == last_date.month:
            end_date = last_date.replace(day=1) - datetime.timedelta(days=1)
        else:
            end_date = last_date
        start_date = end_date.replace(day=1)
        return start_date, end_date

    @BaseTask.set_task_info
    def run(self):
        self.logger.info('估算CPP数量任务开始: %s', self.task_key[1])
        # 最近一个完整月的虚拟机的记录
        qs = models.Detail.objects.filter(
            consumed_service='Microsoft.Compute', meter_category='Virtual Machines',
            date__range=(self.start_date, self.end_date),
            )
        # 虚拟机类型列表
        vm_product_list = qs.values_list('product', flat=True).distinct()
        vm_product_count = vm_product_list.count()
        self.logger.info('当月使用过的的VM机型数量: %s 日期范围(%s ~ %s)', vm_product_count, self.start_date, self.end_date)
        # 锁定配置的CPP记录，下面的for循环里无需更新记录
        locked_product = dict(models.CppProduct.objects.filter(locked=True).values_list('product', 'cpp_count'))

        # 打印进度的时候要用\r开头，期间可能要传入打印日志，插入的日志需要新起一行
        slash_r = False
        count = 0
        indentation = " " * 4
        start_time = time.time()
        for vm_product in vm_product_list:
            count += 1
            if vm_product in locked_product.keys():
                if slash_r:
                    print()
                    slash_r = False
                self.logger.debug('锁定 CPP=%s %s', locked_product[vm_product], vm_product)
                continue
            # PriceSheet表中(report_month, service)会有重复，没有联合唯一
            # 但是针对虚拟机的这部分应该是不重复的
            price = models.PriceSheet.objects.get(
                report_month=self.start_date, 
                service=vm_product,
                )
            # 直接无视度量单位是1000小时的机型，假设这种机型是没有CPP的
            if price.value_of_measure >= 1000:
                continue
            vm_used = qs.filter(
                product=vm_product
                ).aggregate(
                quantity=Sum('consumed_quantity'), cost=Sum('extended_cost')
                )
            # 计算 免费的使用时间
            # 单价: float(price.unit_price / price.value_of_measure)
            # 总时间(quantity) - 总费用(cost) / 单价 = 总时间(quantity) - 收费的使用时间 = 免费的使用时间
            free_hours = (vm_used['quantity'] - vm_used['cost'] / float(price.unit_price / price.value_of_measure))
            # 用满了几个CPP
            cpp_count = int(free_hours / 744) 
            if free_hours - cpp_count * 744 > price.value_of_measure:
                # 除去用满的CPP的时间，剩余的免费时间，如果大于一个度量单位但是依然免费，说明还有一个CPP
                cpp_count += 1
            if cpp_count:
                detail = qs.filter(
                    consumed_service='Microsoft.Compute', meter_category='Virtual Machines',
                    product=vm_product,
                    ).latest('date')
                cpp, created = models.CppProduct.objects.update_or_create(
                    product=detail.product,
                    defaults={
                        'meter_sub_category': detail.meter_sub_category,
                        'meter_name': detail.meter_name,
                        'meter_region': detail.meter_region,
                        'price': price,
                        'cpp_count': cpp_count,
                        },
                    )
                if slash_r:
                    print()
                    slash_r = False
                if created:
                    self.logger.success('新建 CPP=%s %s', cpp_count, cpp.product)
                else:
                    self.logger.info('更新 CPP=%s %s', cpp_count, cpp.product)
            print(
                f'\r{indentation}录入CPP机型进度: {count}/{vm_product_count} {time.time() - start_time:.2f}秒 {vm_product}',
                end='\x1b[K',  # \x1b[K从当前光标位置清除到当前行的末尾
                flush=True)
            slash_r = True
        if slash_r:
            print()
            slash_r = False
        self.logger.info('估算CPP数量任务完成: %s', self.task_key[1])
        

if __name__ == '__main__':
    task = CppSync()
    task.run()

